﻿using RockBus.DataContracts;
using RockBus.ServiceModel;
using RockBus.Transport.Interfaces;
using System;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.Text;

namespace RockBus.Transport.Wcf
{
    public class ServiceEndpointFactory : IServiceEndpointFactory
    {
        private static readonly BindingFactory BindingFactory = new BindingFactory();

        public ServiceEndpoint CreateDynamicServiceEndpoint(ServiceHost serviceHost, EndpointInfo endpointInfo, SecurityMode securityMode = SecurityMode.None)
        {
            return this.CreateDynamicServiceEndpoint(serviceHost, endpointInfo, serviceHost.BaseAddresses.ToArray(), securityMode);
        }

        public ServiceEndpoint CreateDynamicServiceEndpoint(ServiceHost serviceHost, EndpointInfo endpointInfo, Uri[] baseAddresses, SecurityMode securityMode = SecurityMode.None)
        {
            var addressUri = this.GetAbsoluteUri(endpointInfo, baseAddresses);

            var serviceEndpoint = serviceHost.Description.Endpoints.FirstOrDefault((se) => StringComparer.InvariantCultureIgnoreCase.Compare(se.Name, endpointInfo.EndpointConfigurationName) == 0);
            if (serviceEndpoint != null)
            {
                var cd = serviceEndpoint.Contract;
                var endpointAddress = new EndpointAddress(addressUri, serviceEndpoint.Address.Headers.ToArray());
                var se = new ServiceEndpoint(cd, serviceEndpoint.Binding, endpointAddress);
                return se;
            }

            ContractDescription contractDescription = this.GetContractDescription(endpointInfo);

            Binding binding = BindingFactory.CreateBinding(endpointInfo.EndpointName, securityMode);
            var endpointAddress1 = new EndpointAddress(addressUri);

            var se1 = new ServiceEndpoint(contractDescription, binding, endpointAddress1);
            se1.Name = endpointInfo.EndpointConfigurationName;
            return se1;
        }

        public Uri GetAbsoluteUri(EndpointInfo endpointInfo, Uri[] serviceHostBaseAddresses)
        {
            Uri uri = null;

            var scheme = this.GetScheme(endpointInfo);

            var baseAddresses = new UriSchemeKeyedCollection(serviceHostBaseAddresses);
            if (baseAddresses.Contains(scheme) && (baseAddresses[scheme] != null))
            {
                if (!string.IsNullOrEmpty(baseAddresses[scheme].ToString()))
                {
                    uri = ConfigurationHelper.GetUri(baseAddresses[scheme], endpointInfo.Address.Trim('~'));
                }
            }

            if (uri == null)
            {
                uri = new Uri(endpointInfo.Address);
            }

            return this.AdjustUriForMsmq(uri);
        }

        private Uri AdjustUriForMsmq(Uri uri)
        {
            if (uri.Scheme == "net.msmq") //&& !endpoint.Address.Contains("private/"))
            {
                if (uri.Segments[1] != @"private/")
                {
                    var sb = new StringBuilder();
                    sb.AppendFormat(@"net.msmq://{0}/private/", uri.Host);
                    for (int segmentIndex = 1; segmentIndex < uri.Segments.Length; ++segmentIndex)
                    {
                        sb.Append(uri.Segments[segmentIndex]);
                    }
                    var newUri = new Uri(sb.ToString());
                    return newUri;
                }
            }
            return uri;
        }

        public string GetBindingConfigurationName(EndpointInfo endpointInfo)
        {
            string bindingConfigurationName = null;
            switch (endpointInfo.EndpointName)
            {
                case EndpointNameEnum.NetMsmqEndpoint:
                    bindingConfigurationName = "netMsmqBinding";
                    break;

                case EndpointNameEnum.NetNamedPipeEndpoint:
                    bindingConfigurationName = "netNamedPipeBinding";
                    break;

                case EndpointNameEnum.NetTcpEndpoint:
                    bindingConfigurationName = "netTcpBinding";
                    break;

                case EndpointNameEnum.BasicHttpEndpoint:
                    bindingConfigurationName = "basicHttpBinding";
                    break;

                case EndpointNameEnum.WsHttpEndpoint:
                    bindingConfigurationName = "wsHttpBinding";
                    break;

                case EndpointNameEnum.Custom:
                    break;

                case EndpointNameEnum.WebSphereEndpoint:
                    bindingConfigurationName = "webSphereBinding";
                    break;

                case EndpointNameEnum.WebLogicEndpoint:
                    bindingConfigurationName = "webLogicBinding";
                    break;

                case EndpointNameEnum.MetroEndpoint:
                    bindingConfigurationName = "metroBinding";
                    break;

                case EndpointNameEnum.Wso2InteropEndpoint:
                    bindingConfigurationName = "wso2InteropBinding";
                    break;

                default:
                    break;
            }

            return bindingConfigurationName;
        }

        public ContractDescription GetContractDescription(EndpointInfo endpointInfo)
        {
            ContractDescription contractDescription = null;
            switch (endpointInfo.ExchangeType)
            {
                case ExchangeTypeEnum.OneWay:
                case ExchangeTypeEnum.OneWayRequestResponse:
                    contractDescription = ContractDescriptionHelper<IOneWay>.GetContractDescription();
                    break;

                case ExchangeTypeEnum.TwoWayAck:
                case ExchangeTypeEnum.TwoWayAckRequestResponse:
                case ExchangeTypeEnum.TwoWayRequestResponse:
                    contractDescription = ContractDescriptionHelper<ITwoWay>.GetContractDescription();
                    break;

                default:
                    break;
            }
            return contractDescription;
        }

        public Type GetContractType(EndpointInfo endpointInfo)
        {
            Type contractType = null;
            switch (endpointInfo.ExchangeType)
            {
                case ExchangeTypeEnum.OneWay:
                case ExchangeTypeEnum.OneWayRequestResponse:
                    contractType = typeof(IOneWay);
                    break;

                case ExchangeTypeEnum.TwoWayAck:
                case ExchangeTypeEnum.TwoWayAckRequestResponse:
                case ExchangeTypeEnum.TwoWayRequestResponse:
                    contractType = typeof(ITwoWay);
                    break;

                default:
                    break;
            }

            return contractType;
        }

        public string GetScheme(EndpointInfo endpointInfo)
        {
            return BindingFactory.SchemeMap[endpointInfo.EndpointName];
        }
    }
}